/**
 * @class  判断数据类型
 * @author liumouliang 2019-09-09
 * 
 * typeOf  判断数组和null时都是object
 * 
 * Symbol：
 * Symbol.for('test') === Symbol.for('test')
 * Symbol('test') === Symbol('test')
 */
const typeOf = function(obj){
    const toString = Object.prototype.toString; // null没有toString
    // console.log(toString.call(obj));
    const map = {
        '[object Boolean]'  : 'boolean',
        '[object Number]'   : 'number',
        '[object String]'   : 'string',
        '[object Function]' : 'function',
        '[object Array]'    : 'array', //Array.isArray || data.constructor===Array
        '[object Date]'     : 'date',
        '[object RegExp]'   : 'regexp',
        '[object Undefined]': 'undefined',
        '[object Null]'     : 'null',
        '[object Object]'   : 'object',
        '[object Promise]'  : 'promise',
        '[object Map]'      : 'map',
        '[object Set]'      : 'set',
        '[object Map Iterator]' : 'map-iterator',
        '[[object Symbol]]'      : 'symbol', //设置独一无二的常量
        '[object Module]'   : 'module',
    };
    return map[toString.call(obj)] || toString.call(obj);
};
//typeOf(obj)==='array'

/**
 * @class  格式化时间
 * 
 * @param  {Date}    date 
 * @param  {String}    fmt  yyyy-MM-DD hh:mm:ss
 */
const _formatDate = function(date, fmt="yyyy-MM-DD hh:mm:ss"){
    if(typeof date !== "object") date = new Date(date);
    let o = {
        'M+': date.getMonth() + 1, // 月份
        'D+': date.getDate(), // 日
        'h+': date.getHours(), // 小时
        'm+': date.getMinutes(), // 分
        's+': date.getSeconds(), // 秒
        'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
        'S': date.getMilliseconds() // 毫秒
    };

    if (/(y+)/.test(fmt)) {
        fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
    };
    for (let k in o) {
        if (o.hasOwnProperty(k) && new RegExp('(' + k + ')').test(fmt)) {
            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
        };
    };
    return fmt;
};

/**
 * @class  description
 * @author liumouliang 2017-11-23
 *
 * @param  {String}    status  状态码
 * @param  {Object}    options 扩展数据
 * @param  {String}    def     默认提示信息
 * @return {String}
 *
//获取http状态码的提示信息
//http://www.361way.com/statuscode/1139.html
1开头 临时响应   信息，服务器收到请求，需要请求者继续执行操作
2开头 成功，操作被成功接收并处理
3开头 重定向，需要进一步的操作以完成请求
4开头 客户端请求错误，请求包含语法错误或无法完成请求
5开头 服务器错误，服务器在处理请求的过程中发生了错误
 */
const _getHttpCodeMsg = function(status,options={},def="系统异常，请稍后重试"){
    const httpInfo = {
        '400': '错误请求',
        '401': '未授权，请重新登录',
        '403': '拒绝访问',
        '404': '请求错误,未找到该资源',
        '405': '请求方法未允许',
        '408': '系统请求超时，请稍后重试', //请求超时
        '500': '服务器端出错', //内部服务器错误
        '501': '网络未实现',
        '502': '网络错误',
        '503': '服务不可用',
        '504': '网络超时',
        '505': 'http版本不支持该请求',
    };
    return Object.assign(httpInfo,options)[String(status)] || def;
};
//_getHttpCodeMsg(status,{'405':'请求错误,未找到该资源'});

/*
比较字符串版本
v1>v2 为1
~_compareVersion('要验证的','最小版本')
 */
const _compareVersion = (v1, v2) => {
    'use strict';
    v1 = v1.split('.');
    v2 = v2.split('.');
    var len = Math.max(v1.length, v2.length);

    while (v1.length < len) {
        v1.push('0');
    };
    while (v2.length < len) {
        v2.push('0');
    };

    for (var i = 0; i < len; i++) {
        var num1 = parseInt(v1[i]);
        var num2 = parseInt(v2[i]);

        if (num1 > num2) {
            return 1;
        } else if (num1 < num2) {
            return -1;
        };
    };
    return 0;
};

/**
 * @class  获取formData
 * @author liumouliang 2019-06-10
 *
 * @param  {Object}    params 业务参数
 * @param  {Object}    files  {file1: [file],file2:[]}
 * 
 */
const getFormData = (params = {}, files = {}) => {
    let fd = new FormData();
    for(let key in params){
        if(params.hasOwnProperty(key)) fd.append(key,params[key]);
    };
    for(let key in files){
        if(files.hasOwnProperty(key) && files[key] && files[key].length) {
            let fileArr = files[key];
            for (let ix = 0,len = fileArr.length; ix < len; ix++) {
                fd.append(key,fileArr[ix]);
            };
        };
    };
    return fd;
};

/**
 * @class  异步回调
 * @author liumouliang 
 *
 * 1: handleSubmit(resolve, reject) _promise(handleSubmit).then
 * 2: _promise(this.showToast,{title:'hello'}).then //用于小程序等对象参数
 */
function _promise(callback,object) {
    return new Promise(function(resolve, reject){
        if(typeOf(object)==="object"){
            object.success = function(res){
                resolve(res);
            };
            object.fail = function(err){
                reject(err)
            };
            return callback(object);
        }else{
            return callback(resolve, reject);
        };
    });
};

/**
 * @class  文件标准化扩展名-转格式
 * @author liumouliang 2017-11-23
 *
 * @param  {String}    std
 * @return {[type]}
 */
const stdExtName = (std) => {
    'use strict';
    return std.replace(/[_-]||\s/g, '').toLowerCase();
};
//stdExtName('04/2019110405043483.JPG') == 04/2019110405043483.jpg

//浮点数计算
const _float = (f, digit) => {
    'use strict';
    let m = Math.pow(10, digit);
    return Math.round(f * m) / m;
};

/**
 * @param  {[type]}    arr
 * @param  {Function}  fn  function(el,ix,arr)
 * @return {[type]}
 */
const forEach = function(arr,fn,thisValue) {
    let i = -1,len = arr.length;
    while (++i < len) fn.call(thisValue,arr[i],i);
};
const _forObject = function(source,FN,thisValue){
    for(let key in source) source.hasOwnProperty(key) && FN.call(thisValue,source[key],key);
};

const _for = function(source,FN,thisValue){
    if(!FN) return;
    else if(typeOf(source)==="array") return forEach(source,FN,thisValue);
    else if(typeOf(source)==="object") return _forObject(source,FN,thisValue);
};

/**
 * @class  复杂数据处理
 * @author liumouliang 2017-11-23
 *
 * @param  {[type]}    callback  [description]
 * @param  {[type]}    thisValue [description]
 * @return {[type]}
 */
const _filterObject = function(source,callback,thisValue){
    let resObj = {};
    let t = typeOf(callback);
    for(let key in source){
        if(source.hasOwnProperty(key)) {
            if(t==='function' && callback.call(thisValue,source[key],key) || t==='array' && callback.includes(key)) resObj[key] = source[key];
        };
    };
    return resObj;
};
const _filter = function(source,callback,thisValue){
    if(!callback) return source;
    else if(typeOf(source)==="object") return _filterObject(source,callback,thisValue);
    else if(typeOf(source)==="array") return Array.prototype.filter.call(source,callback,thisValue);
};

const _deepCopy = obj => {
    const t = typeOf(obj);
    let result = (t==='array') ? [] : ((t==='object') ? {} : obj);

    if (t === 'array') {
        for (let i = 0; i < obj.length; i++) {
            result.push(_deepCopy(obj[i]));
        }
    } else if ( t === 'object') {
        for (let key in obj) {
            if(obj.hasOwnProperty(key)) result[key] = _deepCopy(obj[key]);
        }
    }
    return result;
};

const _cloneObj = obj => {
    'use strict';
    //obj.constructor == Object
    let newObj = Array.isArray(obj) ? [] : {};
    if (JSON){
        try{
            newObj = JSON.parse(JSON.stringify(obj));
        }catch(err){
            newObj = _deepCopy(obj);
        };
    } else {
        newObj = _deepCopy(obj)
    };
    return newObj
};

export {
    typeOf, //判断数据类型
    _formatDate, //格式化时间
    _getHttpCodeMsg, //获取http状态码的提示信息
    _compareVersion, //比较字符串版本
    getFormData,    // 获取formData
    _promise, //异步回调
    stdExtName, //文件标准化扩展名-转格式 stdExtName('04/3483.JPG')
    _float,     // 浮点数计算 || 保留小数点后几位

    //复杂数据处理
    _filter,
    _filterObject,

    //重写循环
    forEach,
    _forObject,
    _for,

    _deepCopy,
    _cloneObj,
};

export const hasKey = (obj, key) => {
    if (key) return key in obj
    else {
        let keysArr = Object.keys(obj)
        return keysArr.length
    }
}